package net

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"time"
)

const (
	TCP_STATE_CONNECTED    = iota //连接成功
	TCP_STATE_DISCONNECTED        //客户端正常退出
	TCP_STATE_DATA                //接收到数据
	TCP_STATE_ERROR               //服务器accept错误/客户端异常退出（可能服务器异常关闭）/客户端连接失败（即客户端）
)

type StateCall func(conn net.Conn, state uint8, data []byte, datasize uint16, err error)

type TcpServer struct {
	Port    uint16
	OnEvent StateCall
}

func NewTcpServer() *TcpServer {
	return &TcpServer{}
}

// 在协程里执行接收连接，且每个连接为一个协程
func (s *TcpServer) Start(port uint16) error {
	s.Port = port

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
	if err != nil {
		return err
	}

	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				if s.OnEvent != nil {
					s.OnEvent(conn, TCP_STATE_ERROR, nil, 0, err)
				}
				continue
			}
			go s.handleConnection(conn)
		}
	}()
	return nil
}

func (s *TcpServer) handleConnection(conn net.Conn) {
	defer conn.Close()
	if s.OnEvent != nil {
		s.OnEvent(conn, TCP_STATE_CONNECTED, nil, 0, nil)
	}

	if tcpConn, ok := conn.(*net.TCPConn); ok {
		tcpConn.SetNoDelay(true)
		tcpConn.SetReadBuffer(1024 * 32)
		tcpConn.SetWriteBuffer(1024 * 32)            //视频流媒体服务 256K,IM 32K(保持10万并发)
		tcpConn.SetKeepAlive(true)                   // 保持长连接活跃
		tcpConn.SetKeepAlivePeriod(10 * time.Minute) // 减少保活探测频率
	}

	for {
		data, err := ReadPackage(conn)

		if err != nil {
			if err == io.EOF {
				if s.OnEvent != nil {
					s.OnEvent(conn, TCP_STATE_DISCONNECTED, data, 0, err)
				}
			} else {
				if s.OnEvent != nil {
					s.OnEvent(conn, TCP_STATE_ERROR, data, 0, err)
				}
			}
			break
		}
		ss := len(data)

		if s.OnEvent != nil {
			s.OnEvent(conn, TCP_STATE_DATA, data, uint16(ss), nil)
		}
	}
}

// read 4byte data
func ReadUint16(conn net.Conn) (uint16, error) {
	data, err := readBytes(conn, 2)
	if err != nil {
		return 0, err
	}
	return binary.BigEndian.Uint16(data), nil
}

// read n bytes from conn
func readBytes(conn net.Conn, length uint16) ([]byte, error) {
	var data bytes.Buffer
	slen := length
	count := 0
	for {
		buf := make([]byte, slen)
		n, err := conn.Read(buf)
		if err != nil {
			return data.Bytes(), err
		}
		count += n
		data.Write(buf[:n])
		if count >= int(length) {
			break
		}
		slen -= uint16(n)
	}
	return data.Bytes(), nil
}

// 2byte length + data
func ReadPackage(conn net.Conn) ([]byte, error) {
	length, err := ReadUint16(conn)
	if err != nil {
		return nil, err
	}
	data, err := readBytes(conn, length)
	if err != nil {
		return nil, err
	}
	return data, nil
}

// 2byte length + data
func getPackage(buffer *bytes.Buffer, data []byte) {
	binary.Write(buffer, binary.BigEndian, uint16(len(data)))
	buffer.Write(data)
}

// 2byte+[]data,确保数据完整被发送。
func WritePackage(conn net.Conn, data []byte) error {
	bufs := bytes.NewBuffer(nil)
	getPackage(bufs, data)
	res := bufs.Bytes()

	if conn == nil {
		return nil
	}
	buf := res
	for len(buf) > 0 {
		n := 0
		var err error
		//如果是大包，则分包发送,1460是TCP的最大传输单元
		if len(buf) > 1460 {
			n, err = conn.Write(buf[0:1460])
		} else {
			n, err = conn.Write(buf)
		}

		if err != nil {
			return err
		}
		buf = buf[n:]
	}
	return nil
}

// /////////////////////////////////////////////////////////////////////
type TcpClient struct {
	Conn            net.Conn
	OnEvent         StateCall
	AutoCon         bool
	AutoConnectChan chan int
}

func NewTcpClient() *TcpClient {
	return &TcpClient{AutoConnectChan: make(chan int, 100), AutoCon: false}
}

// 发送重连事件，默认自动重连
func (c *TcpClient) postReConnectEvent() {
	go func() {
		if c.AutoCon {
			time.Sleep(3 * time.Second)
			c.AutoConnectChan <- 1
		}
	}()
}

func (c *TcpClient) connectServer(address string) error {
	conn, err := net.Dial("tcp", address)
	if err != nil {
		if c.OnEvent != nil {
			c.OnEvent(nil, TCP_STATE_ERROR, nil, 0, err)
		}
		c.postReConnectEvent()
		return err
	} else {
		c.Conn = conn
		if tcpConn, ok := conn.(*net.TCPConn); ok {
			tcpConn.SetNoDelay(true)
			tcpConn.SetReadBuffer(1024 * 32)
			tcpConn.SetWriteBuffer(1024 * 32)            //视频流媒体服务 256K,IM 32K(保持10万并发)
			tcpConn.SetKeepAlive(true)                   // 保持长连接活跃
			tcpConn.SetKeepAlivePeriod(10 * time.Minute) // 减少保活探测频率
		}
	}
	return nil
}

// 释放客户端
func (c *TcpClient) Release() {
	c.AutoCon = false
	c.close()
	close(c.AutoConnectChan)
}

// 拒绝自动重新连接。
func (c *TcpClient) DisableAutoConnect() {
	c.AutoCon = false
}

// 支持断开后自动重连
func (c *TcpClient) AutoConnectServer(addr string) {
	c.AutoCon = true
	go func() {
		for c.AutoCon {
			if c.connectServer(addr) == nil {
				c.LoopReceive()
			}

			data := <-c.AutoConnectChan
			if data == 0 {
				break
			}
		}
	}()
}

func (c *TcpClient) close() error {
	if c.Conn == nil {
		return fmt.Errorf("client not connected")
	}
	err := c.Conn.Close()
	c.Conn = nil
	return err
}

func (c *TcpClient) LoopReceive() error {
	defer c.close()

	if c.Conn == nil {
		return errors.New("client not connected")
	}

	if c.OnEvent != nil {
		c.OnEvent(c.Conn, TCP_STATE_CONNECTED, nil, 0, nil)
	}

	for {
		data, err := ReadPackage(c.Conn)
		if err != nil {
			if err == io.EOF {
				if c.OnEvent != nil {
					c.OnEvent(c.Conn, TCP_STATE_DISCONNECTED, data, 0, err)
				}
				c.postReConnectEvent()
			} else {
				if c.OnEvent != nil {
					c.OnEvent(c.Conn, TCP_STATE_ERROR, data, 0, err)
					c.postReConnectEvent()
				}
			}
			return err
		}
		if c.OnEvent != nil {
			c.OnEvent(c.Conn, TCP_STATE_DATA, data, uint16(len(data)), nil)
		}
	}
}
